package com.houdehong.wsserver.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocketServer类用于处理WebSocket连接
 * 它注解为@Component，表示它是一个Spring组件
 * 使用@ServerEndpoint注解指定WebSocket的端点路径为"/ws/{sid}"
 */
@Component
@ServerEndpoint("/ws/{sid}")
@Slf4j
public class WebSocketServer {

    // 使用ThreadLocal存储客户端的sid，确保线程安全
    private static final ThreadLocal<String> sidHolder = new ThreadLocal<>();

    // 使用ConcurrentHashMap存储所有活动的WebSocket会话
    private static final ConcurrentHashMap<String, Session> SESSION_MAP = new ConcurrentHashMap<>();

    /**
     * 当WebSocket连接打开时调用的方法
     * 它将sid与对应的Session关联并记录日志
     *
     * @param session 当前WebSocket会话
     * @param sid     客户端的唯一标识符
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        try {
            sidHolder.set(sid);
            SESSION_MAP.put(sid, session);
            log.info("有新连接：sid：{}，sessionId：{}，当前连接数：{}", sid, session.getId(), SESSION_MAP.size());
        } catch (Exception e) {
            log.error("onOpen 发生错误", e);
            throw e; // 确保异常不会被忽略
        }
    }

    /**
     * 当WebSocket连接关闭时调用的方法
     * 它从SESSION_MAP中移除对应的Session并记录日志
     *
     * @param session 当前WebSocket会话
     */
    @OnClose
    public void onClose(Session session) {
        String sid = sidHolder.get();
        if (sid != null) {
            SESSION_MAP.computeIfPresent(sid, (key, value) -> {
                log.info("连接关闭，sid：{}，session id：{}！当前连接数：{}", key, session.getId(), SESSION_MAP.size() - 1);
                return null;
            });
            sidHolder.remove();
        }
    }

    /**
     * 当接收到消息时调用的方法
     * 它处理接收到的消息，并根据消息内容决定是否发送回复
     *
     * @param message 接收到的消息内容
     * @param session 当前WebSocket会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        String sid = sidHolder.get();
        log.info("收到消息：{}，内容：{}", sid, message);
        if ("ping".equals(message)) {
            try {
                session.getBasicRemote().sendText("pong");
            } catch (IOException e) {
                log.error("onMessage 推送消息失败：{}，内容：{}", sid, message);
            }
        } else {
            sendMeasureDataInfo(message, sid);
        }
    }

    /**
     * 当发生错误时调用的方法
     * 它记录错误信息
     *
     * @param session 当前WebSocket会话
     * @param error   发生的错误
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("session id：{} 发生错误", session.getId(), error);
    }

    /**
     * 发送消息给所有连接的客户端，可以选择是否排除发起者
     * 它遍历所有活动的Session，并向每个Session发送消息
     *
     * @param message 要发送的消息内容
     * @param excludeSid 排除的sid，可以为null
     */
    private void sendMeasureDataInfo(String message, String excludeSid) {
        SESSION_MAP.forEach((sid, session) -> {
            if (excludeSid != null && excludeSid.equals(sid)) {
                return;
            }
            try {
                session.getBasicRemote().sendText(message);
                log.info("推送消息：{}，内容：{}", sid, message);
            } catch (IOException e) {
                log.error("推送消息失败：{}，内容：{}", sid, message);
            }
        });
    }

    /**
     * 发送消息给所有连接的客户端
     * 它遍历所有活动的Session，并向每个Session发送消息
     *
     * @param message 要发送的消息内容
     */
    public void sendMeasureDataInfo(String message) {
        sendMeasureDataInfo(message, null);
    }

}
